#include <cstdint>
#include <cstddef>
#include <cstdlib>
#include <fstream>
#include <stlsoft/synch/lock_scope.hpp>
#include <winstl/synch/spin_mutex.hpp>
#include <winstl/shims/conversion/to_uint64.hpp>
#include <windows.h>
extern "C" {
	#include <ntndk.h>
}
#include "module.hpp"

using namespace std;
using stlsoft::lock_scope;
using winstl::spin_mutex;
using stlsoft::to_uint64;

namespace judge {

module mod;

// module initialization
module::module()
	: processor_count_(_processor_count())
{
	DWORD tc = ::GetTickCount();
	srand(static_cast<unsigned>(tc));
	tick_count_prev_.HighPart = 0;
	tick_count_prev_.LowPart = tc;
}

// module uninitialization
module::~module()
{
}

unsigned module::_processor_count()
{
	SYSTEM_INFO system_info;
	::GetSystemInfo(&system_info);
	return static_cast<unsigned>(system_info.dwNumberOfProcessors);
}

unsigned module::processor_count()
{
	return processor_count_;
}

uint64_t module::tick_count()
{
	unsigned long tick_lo = ::GetTickCount();

	lock_scope<spin_mutex> lock(tick_count_mutex_);
	if (tick_lo < tick_count_prev_.LowPart) {
		++tick_count_prev_.HighPart;
	}
	tick_count_prev_.LowPart = tick_lo;
	return stlsoft::to_uint64(tick_count_prev_);
}

uint64_t module::idle_time()
{
	const size_t buffer_length = 328;
	vector<char> buffer(buffer_length);
	PSYSTEM_PERFORMANCE_INFORMATION spi = reinterpret_cast<PSYSTEM_PERFORMANCE_INFORMATION>(&*buffer.begin());

	NTSTATUS status = ::NtQuerySystemInformation(SystemPerformanceInformation, spi, buffer_length, NULL);
	if (!NT_SUCCESS(status)) {
		throw judge_exception(JSTATUS_GENERIC_ERROR);
	}

	return to_uint64(reinterpret_cast<ULARGE_INTEGER &>(spi->IdleProcessTime));
}

bool file_copy(const winstl::path_a &in_path, const winstl::path_a &out_path)
{
	ifstream in(in_path.c_str(), ios::binary);
	if (!in.is_open())
		return false;
	ofstream out(out_path.c_str(), ios::binary);
	const size_t buffer_size = 4096;
	return stream_copy<buffer_size>(in, out);
}

}
